//
//  UtilsExtras.m
//  EVTUtils
//
//  Created by xeonwell on 28/09/2011.
//  Copyright 2011 EVT, Inc. All rights reserved.
//

#import "NSData+UtilsExtras.h"
#import <CommonCrypto/CommonDigest.h>

//从6位模式到ASCII字符的映射
static unsigned char base64EncodeLookup[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

//Base64解码查找映射的“遮蔽”区域的定义
#define xx 65

//从ASCII字符到6位模式的映射
static unsigned char base64DecodeLookup[256] =
{
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63, 
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx, 
    xx,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx, 
    xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 
};

//二进制和BASE64编码/解码单元的基本尺寸
#define BINARY_UNIT_SIZE 3
#define BASE64_UNIT_SIZE 4


#pragma mark - Base64方法加解密
/**
 NewBase64Decode解码
 @param inputBuffer 解码的源ASCII字符串
 @param length 字符串或- 1的长度（要使用strlen）
 @param outputLength 如果不是NULL，输出将包含解码长度。
 @return 解码后的数据
 */
void *NewBase64Decode(const char *inputBuffer, size_t length, size_t *outputLength) {
	if (length == -1) {
		length = strlen(inputBuffer);
	}
	
	size_t outputBufferSize = (length / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE;
	unsigned char *outputBuffer = (unsigned char *)malloc(outputBufferSize);
	
	size_t i = 0;
	size_t j = 0;
	while (i < length) {
        //累积4个有效字符（忽略所有其他字符）
		unsigned char accumulated[BASE64_UNIT_SIZE];
		size_t accumulateIndex = 0;
		while (i < length) {
			unsigned char decode = base64DecodeLookup[inputBuffer[i++]];
			if (decode != xx) {
				accumulated[accumulateIndex] = decode;
				accumulateIndex++;
				
				if (accumulateIndex == BASE64_UNIT_SIZE) {
					break;
				}
			}
		}
        
        //从4个字符中的每一个存储6个字节作为3字节
		outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);
		outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);
		outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];
		j += accumulateIndex - 1;
	}
	
	if (outputLength){
		*outputLength = j;
	}
	return outputBuffer;
}

/**
 NewBase64Decode加密
 @param buffer 编码的源数据
 @param length 字节的输入长度
 @param separateLines 如果为零，则不添加CR/LF字符。否则将每64个编码字符添加一个CR/LF对
 @param outputLength 输出长度，如果不是空，输出将包含编码长度（不包括终止0字符）
 @return 加密数据
 */
char *NewBase64Encode(const void *buffer, size_t length, bool separateLines, size_t *outputLength) {
    
	const unsigned char *inputBuffer = (const unsigned char *)buffer;
	
#define MAX_NUM_PADDING_CHARS 2
#define OUTPUT_LINE_LENGTH 64
#define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / BASE64_UNIT_SIZE) * BINARY_UNIT_SIZE)
#define CR_LF_SIZE 2
	
    //数据大小的字节精确计算
	size_t outputBufferSize = ((length / BINARY_UNIT_SIZE) + ((length % BINARY_UNIT_SIZE) ? 1 : 0)) * BASE64_UNIT_SIZE;
	if (separateLines) {
		outputBufferSize +=
		(outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;
	}
	
	// Include space for a terminating zero
	outputBufferSize += 1;

	//Allocate the output buffer
	char *outputBuffer = (char *)malloc(outputBufferSize);
	if (!outputBuffer) {
		return NULL;
	}
	
	size_t i = 0;
	size_t j = 0;
	const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;
	size_t lineEnd = lineLength;
	
	while (true) {
		if (lineEnd > length) {
			lineEnd = length;
		}
		
		for (; i + BINARY_UNIT_SIZE - 1 < lineEnd; i += BINARY_UNIT_SIZE) {
			//Inner loop: turn 48 bytes into 64 base64 characters
			outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
			outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
												   | ((inputBuffer[i + 1] & 0xF0) >> 4)];
			outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)
												   | ((inputBuffer[i + 2] & 0xC0) >> 6)];
			outputBuffer[j++] = base64EncodeLookup[inputBuffer[i + 2] & 0x3F];
		}
		
		if (lineEnd == length) {
			break;
		}

		//Add the newline
		outputBuffer[j++] = '\r';
		outputBuffer[j++] = '\n';
		lineEnd += lineLength;
	}
	
	if (i + 1 < length) {
		//Handle the single '=' case
		outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
		outputBuffer[j++] = base64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
											   | ((inputBuffer[i + 1] & 0xF0) >> 4)];
		outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];
		outputBuffer[j++] =	'=';
	}
	else if (i < length) {
		//Handle the double '=' case
		outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
		outputBuffer[j++] = base64EncodeLookup[(inputBuffer[i] & 0x03) << 4];
		outputBuffer[j++] = '=';
		outputBuffer[j++] = '=';
	}
	outputBuffer[j] = 0;
	
	//Set the output length and return the buffer
	if (outputLength) {
		*outputLength = j;
	}
	return outputBuffer;
}

@implementation NSData (UtilsExtras)
#pragma mark - md5加密
- (NSString *)md5Hash {
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5([self bytes], (uint32_t)[self length], result);
    
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11], result[12], result[13], result[14],
            result[15]
            ];
}

#pragma mark - base64与NSData互转
//Base64String转成NSData（解码）
+ (NSData *)dataFromBase64String:(NSString *)aString {
	NSData *data = [aString dataUsingEncoding:NSASCIIStringEncoding];
	size_t outputLength;
	void *outputBuffer = NewBase64Decode([data bytes], [data length], &outputLength);
	NSData *result = [NSData dataWithBytes:outputBuffer length:outputLength];
	free(outputBuffer);
	return result;
}

//NSData转成Base64String（加密）
- (NSString *)base64EncodedString {
	size_t outputLength;
	char *outputBuffer = NewBase64Encode([self bytes], [self length], true, &outputLength);
	NSString *result = nil;
    if (outputBuffer != NULL)
    {
        result = [[NSString alloc] initWithBytes:outputBuffer
                                           length:outputLength
                                         encoding:NSASCIIStringEncoding];
                             
        free(outputBuffer);
    }
	return result;
}

#pragma mark - 网址编码
- (NSString *)urlEncodedString {
	char *hex = "0123456789ABCDEF";
	unsigned char * data = (unsigned char*)[self bytes];
	uint32_t len = (uint32_t)[self length];
	NSMutableString* s = [NSMutableString string];
	for(int i = 0;i<len;i++){
		unsigned char c = data[i];
		if( ('a' <= c && c <= 'z')
		   || ('A' <= c && c <= 'Z')
		   || ('0' <= c && c <= '9') ){
			NSString* ts = [[NSString alloc] initWithCString:(char *)&c encoding:NSUTF8StringEncoding];
			
			[s appendString:ts];
		} else {
			[s appendString:@"%"];
			char ts1 = hex[c >> 4];
			NSString* ts = [[NSString alloc] initWithCString:&ts1 encoding:NSUTF8StringEncoding];
			[s appendString:ts];
			char ts2 = hex[c & 15];
			ts = [[NSString alloc] initWithCString:&ts2 encoding:NSUTF8StringEncoding];
			[s appendString:ts];
			
		}
	}
	return s;
}


@end
